package com.tinem.platform.web.gateway.filter;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.tinem.platform.module.commons.util.SessionUtil;
import com.tinem.platform.module.pojo.co.GatewayHeadName;
import com.tinem.platform.module.pojo.enums.common.Enabled;
import com.tinem.platform.module.pojo.vo.error.UserException;
import com.tinem.platform.module.starter.jpa.entity.config.ConfigWebGatewayUrlLogin;
import com.tinem.platform.module.starter.jpa.repository.config.ConfigWebGatewayUrlLoginRepository;
import com.tinem.platform.module.starter.sdk.MessageCode;
import com.tinem.platform.web.gateway.util.GatewayUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;

/**
 * @author fengzhihao
 * @version v1
 * @program: platform
 * @className ChannelScopeGatewayFilterFactory
 * @description 客户端请求签名
 * @site 
 * @company 
 * @create 2020-07-03 12:26
 */
@Slf4j
@Component
public class LoginGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> implements ApplicationRunner {

    Cache<String, Boolean> lfuCache = CacheUtil.newLFUCache(100000);

    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    ConfigWebGatewayUrlLoginRepository configWebGatewayUrlLoginRepository;

    AntPathMatcher antPathMatcher = new AntPathMatcher();
    List<ConfigWebGatewayUrlLogin> configWebGatewayUrlLoginList;

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {

            String service = exchange.getRequest().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_SERVICE);
            String method = exchange.getRequest().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_METHOD);
            Boolean login = lfuCache.get(service+"&"+method);
            String jit = GatewayUtil.getJit(exchange);
            String jitRedisKey = StrUtil.isEmpty(jit)?null:SessionUtil.getRedisKeySessionId(jit);
            // 不需要登录
            if(login != null){
                // 接口需要登录
                if(login){
                    // 如果存在jit
                    if(StrUtil.isNotEmpty(jitRedisKey)){
                        // 判断session 是存在
                        if(stringRedisTemplate.hasKey(jitRedisKey)){
                            return chain.filter(exchange);
                        }
                    }
                    // 如果不存在jit或者session不存在
                    log.warn("user not login; service:({});method:({});jit:({});",service,method,jit);
                    throw new UserException(MessageCode.ERROR_SESSION_NOT_FIND);
                }
            }
            Optional<ConfigWebGatewayUrlLogin> optional = configWebGatewayUrlLoginList.stream()
                    .filter(configUrlLogin -> ReUtil.isMatch(configUrlLogin.getService(),service))
                    .filter(configUrlLogin -> antPathMatcher.match(configUrlLogin.getMethod(),method))
                    .findFirst();
            // 如果数据库规则未命中
            if(!optional.isPresent()){
                // 记录此请求不需要登录
                lfuCache.put(service+"&"+method,false);
                return chain.filter(exchange);
            }
            ConfigWebGatewayUrlLogin configUrlLogin = optional.get();
            if(!configUrlLogin.getLogin()){
                lfuCache.put(service+"&"+method,false);
                return chain.filter(exchange);
            }
            lfuCache.put(service+"&"+method,true);
            if(StrUtil.isNotEmpty(jit)){
                if(stringRedisTemplate.hasKey(jitRedisKey)){
                    return chain.filter(exchange);
                }
            }
            log.warn("user not login; service:({});method:({});jit:({});",service,method,jit);
            throw new UserException(MessageCode.ERROR_SESSION_NOT_FIND);
        };
    }
    @Scheduled(cron = "0 */5 * * * ?")
    public void refresh(){
        configWebGatewayUrlLoginList = configWebGatewayUrlLoginRepository.findByStatusOrderByOrderDesc(Enabled.ENABLED);
    }

    public void clearCache(){
        lfuCache = CacheUtil.newLFUCache(100000);
    }
    @Override
    public void run(ApplicationArguments args) throws Exception {
        refresh();
    }
}
